import {
    Component,
    Input,
    OnInit,
    OnDestroy,
    ChangeDetectorRef,
    ViewChild
} from '@angular/core';
import { Subscription , timer} from "rxjs";

import { VULNERABILITY_SCAN_STATUS } from '../utils';
import {
    VulnerabilitySummary,
    TagService,
    ScanningResultService,
    Tag
} from '../service/index';
import { ErrorHandler } from '../error-handler/index';
import { toPromise } from '../utils';
import { ChannelService } from '../channel/index';
import { JobLogService } from "../service/index";

const STATE_CHECK_INTERVAL: number = 2000; // 2s
const RETRY_TIMES: number = 3;

@Component({
    selector: 'hbr-vulnerability-bar',
    templateUrl: './result-bar-chart-component.html',
    styleUrls: ['./scanning.scss']
})
export class ResultBarChartComponent implements OnInit, OnDestroy {
    @Input() repoName: string = "";
    @Input() tagId: string = "";
    @Input() summary: VulnerabilitySummary;
    onSubmitting: boolean = false;
    retryCounter: number = 0;
    stateCheckTimer: Subscription;
    scanSubscription: Subscription;
    timerHandler: any;

    constructor(
        private tagService: TagService,
        private scanningService: ScanningResultService,
        private errorHandler: ErrorHandler,
        private channel: ChannelService,
        private ref: ChangeDetectorRef,
        private jobLogService: JobLogService,
    ) { }

    ngOnInit(): void {
        this.scanSubscription = this.channel.scanCommand$.subscribe((tagId: string) => {
            let myFullTag: string = this.repoName + "/" + this.tagId;
            if (myFullTag === tagId) {
                this.scanNow();
            }
        });
    }

    ngOnDestroy(): void {
        if (this.stateCheckTimer) {
            this.stateCheckTimer.unsubscribe();
            this.stateCheckTimer = null;
        }
        if (this.scanSubscription) {
            this.scanSubscription.unsubscribe();
        }
    }

    // Get vulnerability scanning status
    public get status(): string {
        if (this.summary && this.summary.scan_status) {
            return this.summary.scan_status;
        }

        return VULNERABILITY_SCAN_STATUS.stopped;
    }

    public get completed(): boolean {
        return this.status === VULNERABILITY_SCAN_STATUS.finished;
    }

    public get error(): boolean {
        return this.status === VULNERABILITY_SCAN_STATUS.error;
    }

    public get queued(): boolean {
        return this.status === VULNERABILITY_SCAN_STATUS.pending;
    }

    public get scanning(): boolean {
        return this.status === VULNERABILITY_SCAN_STATUS.running;
    }

    public get stopped(): boolean {
        return this.status === VULNERABILITY_SCAN_STATUS.stopped;
    }

    public get unknown(): boolean {
        return this.status === VULNERABILITY_SCAN_STATUS.unknown;
    }

    scanNow(): void {
        if (this.onSubmitting) {
            // Avoid duplicated submitting
            return;
        }

        if (!this.repoName || !this.tagId) {
            return;
        }

        this.onSubmitting = true;

        toPromise<any>(this.scanningService.startVulnerabilityScanning(this.repoName, this.tagId))
            .then(() => {
                this.onSubmitting = false;

                // Forcely change status to queued after successful submitting
                this.summary = {
                    scan_status: VULNERABILITY_SCAN_STATUS.pending,
                    severity: null,
                    components: null,
                    update_time: null
                };

                // Forcely refresh view
                this.forceRefreshView(1000);

                // Start check status util the job is done
                if (!this.stateCheckTimer) {
                    // Avoid duplicated subscribing
                    this.stateCheckTimer = timer(STATE_CHECK_INTERVAL, STATE_CHECK_INTERVAL).subscribe(() => {
                        this.getSummary();
                    });
                }
            })
            .catch(error => {
                this.onSubmitting = false;
                this.errorHandler.error(error);
            });
    }

    getSummary(): void {
        if (!this.repoName || !this.tagId) {
            return;
        }

        toPromise<Tag>(this.tagService.getTag(this.repoName, this.tagId))
            .then((t: Tag) => {
                // To keep the same summary reference, use value copy.
                this.copyValue(t.scan_overview);

                // Forcely refresh view
                this.forceRefreshView(1000);

                if (!this.queued && !this.scanning) {
                    // Scanning should be done
                    if (this.stateCheckTimer) {
                        this.stateCheckTimer.unsubscribe();
                        this.stateCheckTimer = null;
                    }
                }
            })
            .catch(error => {
                this.errorHandler.error(error);
                this.retryCounter++;
                if (this.retryCounter >= RETRY_TIMES) {
                    // Stop timer
                    if (this.stateCheckTimer) {
                        this.stateCheckTimer.unsubscribe();
                        this.stateCheckTimer = null;
                    }
                    this.retryCounter = 0;
                }
            });
    }

    copyValue(newVal: VulnerabilitySummary): void {
        if (!this.summary || !newVal || !newVal.scan_status) { return; }
        this.summary.scan_status = newVal.scan_status;
        this.summary.job_id = newVal.job_id;
        this.summary.severity = newVal.severity;
        this.summary.components = newVal.components;
        this.summary.update_time = newVal.update_time;
    }

    forceRefreshView(duration: number): void {
        // Reset timer
        if (this.timerHandler) {
            clearInterval(this.timerHandler);
        }
        this.timerHandler = setInterval(() => this.ref.markForCheck(), 100);
        setTimeout(() => {
            if (this.timerHandler) {
                clearInterval(this.timerHandler);
                this.timerHandler = null;
            }
        }, duration);
    }

    viewLog(): string {
        return this.jobLogService.getScanJobBaseUrl() + "/" + this.summary.job_id + "/log";
    }
}
